package process

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"hetao.com/pressure/request"
	"hetao.com/pressure/utils"
)

const POST = "POST"
const GET = "GET"

var (
	// 输出统计数据的时间
	exportStatisticsTime = 1 * time.Second
	requestTimeList      []uint64 // 所有请求响应时间
)

//ProcessTask 任务对象
type ProcessTask struct {
	ConnectCount uint64
	SendCount    uint64
	Url          string
	Method       string
	Start        uint64
	End          uint64
}

//Process 开始执行
func (p *ProcessTask) Process() error {
	var wg sync.WaitGroup
	var statWg sync.WaitGroup
	ch := make(chan *request.RequestResult, 1000)
	stopCh := make(chan bool)
	statWg.Add(1)
	go p.StatProcess(&statWg, ch, stopCh)

	for i := uint64(0); i < p.ConnectCount; i++ {
		wg.Add(1)
		go singleProcess(&SingleReq{
			Number: i, Url: p.Url, Method: p.Method, SendCount: p.SendCount,
		}, &wg, ch)
	}

	wg.Wait()
	stopCh <- true

	statWg.Wait()
	close(ch)
	utils.Logger("-------完成---------")
	return nil
}

type SingleReq struct {
	SendCount uint64
	Url       string
	Method    string
	Number    uint64
}

func singleProcess(req *SingleReq, wg *sync.WaitGroup, ch chan *request.RequestResult) {
	defer func() {
		wg.Done()
	}()
	client := request.HttpRequest{}
	client.Init()
	for i := uint64(0); i < req.SendCount; i++ {
		var rst *request.RequestResult
		if POST == req.Method {
			rst = client.Post(req.Url, "")
		} else {
			rst = client.Get(req.Url)
		}
		rst.No = i
		ch <- rst
	}
}

func (p *ProcessTask) StatProcess(wg *sync.WaitGroup, ch chan *request.RequestResult, stopStat chan bool) {
	var stopChan = make(chan bool)
	defer func() {
		wg.Done()
		stopChan <- true
	}()

	// 时间
	var (
		processingTime uint64 // 处理总时间
		maxTime        uint64 // 最大时长
		minTime        uint64 // 最小时长
		successNum     uint64 // 成功处理数，code为0
		failureNum     uint64 // 处理失败数，code不为0
		chanIDLen      int    // 并发数
		chanIDs        = make(map[uint64]bool)
		receivedBytes  uint64
		mutex          = sync.RWMutex{}
	)

	p.Start = uint64(time.Now().UnixMilli())
	// 错误码/错误个数
	var errCode = &sync.Map{}
	// 定时输出一次计算结果
	ticker := time.NewTicker(exportStatisticsTime)
	go func() {
		for {
			select {
			case <-ticker.C:
				p.End = uint64(time.Now().UnixMilli())
				mutex.Lock()
				go calculateData(p.ConnectCount, processingTime, p.End-p.Start, maxTime, minTime, successNum, failureNum,
					chanIDLen, errCode, receivedBytes)
				mutex.Unlock()
			case <-stopChan:
				// 处理完成
				return
			}
		}
	}()
	header()

	for {
		select {
		case dd, ok := <-ch:
			if ok {
				mutex.Lock()
				// fmt.Println("处理一条数据")
				processingTime = processingTime + dd.Cost
				if maxTime <= dd.Cost {
					maxTime = dd.Cost
				}
				if minTime == 0 {
					minTime = dd.Cost
				} else if minTime > dd.Cost {
					minTime = dd.Cost
				}
				// 是否请求成功
				if dd.Code == 200 {
					successNum = successNum + 1
				} else {
					failureNum = failureNum + 1
				}
				// 统计错误码
				if value, ok := errCode.Load(dd.Code); ok {
					valueInt, _ := value.(int)
					errCode.Store(dd.Code, valueInt+1)
				} else {
					errCode.Store(dd.Code, 1)
				}
				receivedBytes += dd.ReceivedBytes
				if _, ok := chanIDs[dd.No]; !ok {
					chanIDs[dd.No] = true
					chanIDLen = len(chanIDs)
				}
				requestTimeList = append(requestTimeList, dd.Cost)
				mutex.Unlock()
			}

		case <-time.After(time.Second * 3):
			p.PrintEnd(processingTime, successNum, failureNum,
				chanIDLen, errCode, receivedBytes)
			return
		case <-stopStat:
			p.End = uint64(time.Now().UnixMilli())
			time.Sleep(100)
			calculateData(p.ConnectCount, processingTime, p.End-p.Start, maxTime, minTime, successNum, failureNum, chanIDLen, errCode, receivedBytes)

			p.PrintEnd(processingTime, successNum, failureNum,
				chanIDLen, errCode, receivedBytes)
			return
		}
	}
}

func (p *ProcessTask) PrintEnd(processingTime, successNum, failureNum uint64, chanIDLen int, errCode *sync.Map, receivedBytes uint64) {
	requestTime := p.End - p.Start
	fmt.Printf("\n\n")
	fmt.Println("*************************  结果 stat  ****************************")
	fmt.Println("处理协程数量:", p.ConnectCount)
	// fmt.Println("处理协程数量:", concurrent, "程序处理总时长:", fmt.Sprintf("%.3f", float64(processingTime/concurrent)/1e9), "秒")
	fmt.Println("请求总数（并发数*请求数 -c * -n）:", successNum+failureNum, "总请求时间:",
		fmt.Sprintf("%.3f", float64(requestTime)/1e3),
		"秒", "successNum:", successNum, "failureNum:", failureNum)
	printTop(requestTimeList)
	fmt.Println("*************************  结果 end   ****************************")
	fmt.Printf("\n\n")
}

// printTop 排序后计算 top 90 95 99
func printTop(requestTimeList []uint64) {
	if requestTimeList == nil {
		return
	}

	all := requestTimeList
	sort.Slice(all, func(i, j int) bool { return all[i] < all[j] })
	// fmt.Println("tp90:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.90)]/1e3)))
	// fmt.Println("tp95:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.95)]/1e3)))
	// fmt.Println("tp99:", fmt.Sprintf("%.3f", float64(all[int(float64(len(all))*0.99)]/1e3)))

	top := func(perent float64) uint64 {
		return all[int(float64(len(all))*perent)]
	}
	fmt.Println("tp90:", fmt.Sprintf("%d ms", top(0.90)))
	fmt.Println("tp95:", fmt.Sprintf("%d ms", top(0.95)))
	fmt.Println("tp99:", fmt.Sprintf("%d ms", top(0.99)))
}

// calculateData 计算数据
func calculateData(concurrent, processingTime, requestTime, maxTime, minTime, successNum, failureNum uint64,
	chanIDLen int, errCode *sync.Map, receivedBytes uint64) {
	if processingTime == 0 {
		processingTime = 1
	}
	var (
		qps              float64
		averageTime      float64
		requestTimeFloat float64
	)
	// 平均 每个协程成功数*总协程数据/总耗时 (每秒)
	if processingTime != 0 {
		qps = float64(successNum*concurrent*1e3) / float64(processingTime)
	}
	// 平均时长 总耗时/总请求数/并发数 纳秒=>毫秒
	if successNum != 0 && concurrent != 0 {
		averageTime = float64(processingTime) / float64(successNum)
	}
	// 纳秒=>毫秒

	requestTimeFloat = float64(requestTime) / 1e3
	// 打印的时长都为毫秒
	table(successNum, failureNum, maxTime, minTime, errCode, qps, averageTime, requestTimeFloat, chanIDLen,
		receivedBytes)
}

// header 打印表头信息
func header() {
	fmt.Printf("\n\n")
	// 打印的时长都为毫秒 总请数
	fmt.Println("─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────")
	fmt.Println(" 耗时│ 并发数│ 成功数│ 失败数│   qps  │最长耗时│最短耗时│平均耗时│下载字节│字节每秒│ 状态码")
	fmt.Println("─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────")
	return
}

// table 打印表格
func table(successNum, failureNum, maxTime, minTime uint64, errCode *sync.Map,
	qps, averageTime, requestTimeFloat float64, chanIDLen int, receivedBytes uint64) {
	var (
		speed int64
	)
	if requestTimeFloat > 0 {
		speed = int64(float64(receivedBytes) / requestTimeFloat)
	} else {
		speed = 0
	}
	var (
		receivedBytesStr string
		speedStr         string
	)
	// 判断获取下载字节长度是否是未知
	if receivedBytes <= 0 {
		receivedBytesStr = ""
		speedStr = ""
	} else {
		receivedBytesStr = fmt.Sprintf("%d", receivedBytes)
		speedStr = fmt.Sprintf("%d", speed)
	}
	// 打印的时长都为毫秒
	result := fmt.Sprintf("%4.0fs│%7d│%7d│%7d│%8.2f│%8.2d│%8.d│%8.f│%8s│%8s│%v",
		requestTimeFloat, chanIDLen, successNum, failureNum, qps, maxTime, minTime, averageTime,
		receivedBytesStr, speedStr,
		printMap(errCode))
	fmt.Println(result)
	return
}

// printMap 输出错误码、次数 节约字符(终端一行字符大小有限)
func printMap(errCode *sync.Map) (mapStr string) {
	var (
		mapArr []string
	)
	errCode.Range(func(key, value interface{}) bool {
		mapArr = append(mapArr, fmt.Sprintf("%v:%v", key, value))
		return true
	})
	sort.Strings(mapArr)
	mapStr = strings.Join(mapArr, ";")
	return
}
